/** \file
* POSIX-specific implementation of the Thread class.
* This file is part of Chorus, available at
* http://code.google.com/p/chorus-twitter/ . This file and all associated files
* and derivative works are bound by the terms of the LGPL 3.0 license,
* available at http://www.gnu.org/licenses/lgpl-3.0-standalone.html .
* \par Subversion URL:
* \$URL: http://chorus-twitter.googlecode.com/svn/branches/cpp/src/system/posix/Thread.cpp $
* \version \$Rev: 153 $
* \date    \$Date: 2010-04-06 07:43:42 +0000 (Tue, 06 Apr 2010) $
*/

#define NEEDS_POSIX
#include "config.h"

#include <errno.h>
#include <signal.h>

#include "system/Timer.hpp"
#include "system/DebugConsole.hpp"
#include "system/Thread.hpp"

using namespace std;

bool Thread::init()
{
	thread = 0;

	// Create the attributes for the thread such that it's initially detached.
	// Apparently, this lets the system free memory associated with the thread
	// automatically when it exits.
	int result;
	if (result = pthread_cond_init(&cond, 0))
	{
		PDEBUG_C_CODE("pthread_cond_init failed", result);
		return false;
	}
	if (result = pthread_attr_init(&threadattr))
	{
		PDEBUG_C_CODE("pthread_attr_init failed", result);
		return false;
	}
	if (result = pthread_attr_setdetachstate(&threadattr, PTHREAD_CREATE_DETACHED))
	{
		PDEBUG_C_CODE("pthread_attr_setdetachstate failed", result);
		return false;
	}
	return true;
}

bool Thread::uninit()
{
	bool success = true;
	// Destroy the condition variable and attribute objects.
	int result;
	if (result = pthread_attr_destroy(&threadattr))
	{
		PDEBUG_C_CODE("pthread_attr_destroy failed", result);
		success = false;
	}
	if (result = pthread_cond_destroy(&cond))
	{
		PDEBUG_C_CODE("pthread_cond_destroy failed", result);
		success = false;
	}
	return success;
}

bool Thread::start()
{
	if (!isDone())
	{
		PDEBUG2("Thread already started:", (void*)thread);
		return false;
	}

	// Use the existing attribute object to create the thread.
	int result;
	if (result = pthread_create(&thread, &threadattr, callback, this))
	{
		PDEBUG_C_CODE("pthread_create failed", result);
		return false;
	}

	return true;
}

bool Thread::pause()
{
	return false;
}

bool Thread::resume()
{
	return false;
}

bool Thread::abort()
{
	if (isDone())
		return true;
	PDEBUG2("Warning - thread aborted while still running:", (void*)thread);

	int result;
	if (result = pthread_kill(thread, SIGABRT))
	{
		PDEBUG_C_CODE("pthread_kill failed", result);
		return false;
	}

	// The way we now have it set up with a condition variable, there is no
	// longer a mutex that gets orphaned when a thread is aborted. This also
	// makes it portable to macs. Yay!

	thread = 0;
	return true;
}

bool Thread::wait()
{
	if (isDone())
		return true;

	// This lock should usually be instantaneous.
	if (!mutex.lock())
		return false;

	bool success = true;

	// Wait on the condition variable. We'll block here until the thread calls
	// a condition variable "broadcast".
	const int result = pthread_cond_wait(&cond, &mutex.getMutex());
	success &= mutex.unlock();
	if (result)
	{
		PDEBUG_C_CODE("pthread_cond_wait failed", result);
		success = false;
	}

	return success;
}

bool Thread::waitThenAbort(unsigned timeoutms)
{
	return waitThenAbort(Timer::msToTs(timeoutms));
}

bool Thread::waitThenAbort(timespec timeoutts)
{
	if (isDone())
		return true;

	// This lock should usually be instantaneous.
	if (!mutex.lock())
		return false;

	bool success = true;

	// Wait on the condition variable. We'll block here until the thread calls
	// a condition variable "broadcast", or the wait times out.
	const int result = pthread_cond_timedwait(&cond, &mutex.getMutex(),
		&timeoutts);
	success &= mutex.unlock();
	switch (result)
	{
	case 0:
		break;
	case ETIMEDOUT:
		// If we timed out, consider the thread stalled and abort it.
		PDEBUG2("Warning - Thread timeout:", (void*)thread);
		success &= abort();
		break;
	default:
		PDEBUG_C_CODE("pthread_cond_timedwait failed", result);
		success = false;
	}

	return success;
}

bool Thread::waitThenAbort(std::list<Thread*> &threads,
	unsigned timeoutms)
{
	bool success = true;

	const timespec now = timerglob->getTimeSpec();
	// Expiry is an absolute timestamp, so it makes it easy for us to use it in
	// every wait call in the loop.
	const timespec expiry = Timer::addSpecMs(now, timeoutms);

	// Wait on all of the threads.
	for (std::list<Thread*>::iterator i = threads.begin();
		i != threads.end();
		i++)
		success &= (*i)->waitThenAbort(expiry);

	return success;
}

unsigned Thread::getCurrentThreadID()
{
	return (unsigned)pthread_self();
}

bool Thread::isDone()
{
	return !thread;
}

void *Thread::callback(void *param)
{
	// param is a pointer to a Thread instance. Call its thread callback
	// function.
	return (void*)(((Thread*)param)->callFunc());
}

bool Thread::callFunc()
{
	// Most thread time is spent in here.
	bool success = call->call();
	thread = 0;

	// Lock the thread. This should be instantaneous.
	success &= mutex.lock();
	// Send any blocked threads a broadcast to release them.
	int result;
	if (result = pthread_cond_broadcast(&cond))
	{
		PDEBUG_C_CODE("pthread_cond_broadcast failed", result);
		success = false;
	}
	success &= mutex.unlock();

	return success;
}
